﻿<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>SetList - Vanilla Extension</title>
<style type="text/css"><!--
dd { margin-bottom: 1em; }
blockquote { margin: 1em; padding: 1em; border: 1px solid black; }
--></style>
</head>

<body>

<h1 id="top">SetList</h1>
<p>SetList is a Vanilla extension that helps extension authors to quickly
and easily create a form for their extension's settings. By writing a
simple INI file, any extension developer can have clean and correct form
on the Vanilla settings page without a single extra line of PHP code.</p>

<h2 id="toc">Table of Contents</h2>
<ul>
<li><a href="#whatitis">What SetList Does</a></li>
<li><a href="#install">Installing and Enabling SetList</a></li>
<li><a href="#extensions">How Extension Writers Use SetList</a></li>
<li><a href="#translators">How Translators Use SetList</a></li>
<li><a href="#themes">How Theme Writers Use SetList</a></li>
<li><a href="#uninstall">Uninstalling SetList</a></li>
<li><a href="#develop">Development</a></li>
</ul>

<hr />
<h2 id="whatitis">What SetList Does</h2>
<p>By itself, SetList does nothing. Installing SetList on a clean copy of
Vanilla with no other extensions will have absolutely no noticable effect.</p>
<p>SetList will only take action when it finds a "SetList.ini" file inside
another extension's directory. SetList will read the INI file and use it to
build a settings form for that extension. The form will be accessible in the
Panel of the Settings page, under the header "Extension Settings" with the
extension's name as the link. When the form is submitted, SetList will save
the settings to Vanilla's global configuration.</p>
<p>Extension writers can customize the form even further by adding Init and
Process delegates to SetList with extra form logic. All text on the form is
registered as definitions in the Context to make life easier for translators.
Theme writers can override the form template to ensure than extension
settings forms don't break their themes.</p>
<p><strong>Extensions do not require SetList!</strong> Even if SetList is
uninstalled - or never installed at all - forum admins can still use a
SetList-aware extension normally, specifying setting values by changing the
Vanilla settings file directly.</p>

<hr />
<h2 id="install">Installing and Enabling SetList</h2>
<p>In order for Vanilla to recognize an extension, it must be contained within
its own directory within the extensions directory. So, once you have
downloaded and unzipped the SetList files, you can then place the folder
containing the default.php file into your installation of Vanilla. The path to
SetList's default.php file should look like this:</p>
<blockquote><code>/path/to/vanilla/extensions/SetList/default.php</code></blockquote>
<p>Once this is complete, you can enable SetList through the "Manage Extensions"
form on the settings tab in Vanilla.</p>

<hr />
<h2 id="extensions">How Extension Writers Use SetList</h2>
<h3>Starting Quickly</h3>
<p>Using SetList to create a settings form can be as easy as writing a
<code>SetList.ini</code> file in your extension's directory:</p>
<blockquote><pre>
elements.MyHeader.type = "header"
elements.MyHeader.label = "This is a Header"
elements.MyHeader.description = "This is text describing the stuff here."

elements.MyCheckbox.type = "checkbox"
elements.MyCheckbox.label = "Check this box"
elements.MyCheckbox.description = "If you check this box, it will be checked."

elements.MyText.type = "text"
elements.MyText.label = "Some text here"
elements.MyText.description = "This is a single line text input."

elements.MyTextarea.type = "textarea"
elements.MyTextarea.label = "Lots of text here"
elements.MyTextarea.description = "This is a 15-line text box."

elements.MySelect.type = "select"
elements.MySelect.label = "Select something"
elements.MySelect.description = "This is a select box."
elements.MySelect.options.First.label = "First"
elements.MySelect.options.First.value = "1"
elements.MySelect.options.Second.label = "Second"
elements.MySelect.options.Second.value = "2"
elements.MySelect.options.Third.label = "Third"
elements.MySelect.options.Third.value = "3"

elements.MyFile.type = "file"
elements.MyFile.label = "Upload File"
elements.MyFile.description = "Upload a file here."

elements.MyHidden.type = "hidden"
elements.MyHidden.value = "Something sneaky"
</pre></blockquote>
<p>Go ahead, copy and paste that into a file named <code>SetList.ini</code>
in your extension's directory. It will work, and it will explain the process
much better than I could.</p>
<p><i>If you think this INI looks a lot like how you would create a form in
Zend Framework, gold star for you. SetList does not use ZF but it does use a
very simplified version of ZF's config format.</i></p>
<p>Note that you don't have to specify an element's description, it's
optional. However, if you don't specify an element's label you will get a
default label of that element's key name, such as <code>MyText</code>.</p>
<h3>More Advanced</h3>
<p>Right now the values on this form will only load from and save to the
global Vanilla configuration. You may want to add some extra logic via
delegate functions in your extension's <code>default.php</code> file:</p>
<blockquote><pre>
function SetList_Init_MyExt(&amp;$SetList)
{
   $form = &amp;$SetList-&gt;DelegateParameters['Form'];

   $SetList-&gt;Context-&gt;SetDefinition('MyExt.IP', 'Your IP address');

   $form['elements']['MyText']['label'] =
      $SetList-&gt;Context-&gt;GetDefinition('MyExt.IP');
   $form['elements']['MyText']['value'] = GetRemoteIp();
}
$Context-&gt;AddToDelegate('SetList', 'Init_MyExt', 'SetList_Init_MyExt');

function SetList_Process_MyExt(&amp;$SetList)
{
   $form = &amp;$SetList-&gt;DelegateParameters['Form'];

   $SetList-&gt;Context-&gt;SetDefinition('MyExt.ErrorThird',
      'You must choose the third option because I say so.');
   $SetList-&gt;Context-&gt;SetDefinition('MyExt.FileSize', 'File size: ');

   if ( 3 != $form['elements']['MySelect']['value'] ) {
      $SetList-&gt;Context-&gt;WarningCollector-&gt;Add(
         $SetList-&gt;Context-&gt;GetDefinition('MyExt.ErrorThird'));
      $form['elements']['MySelect']['value'] = 3;
   }

   if ( !empty($form['elements']['MyFile']['fileinfo']) ) {
      $form['elements']['MyFile']['description'] =
         $SetList-&gt;Context-&gt;GetDefinition('MyExt.FileSize')
         . $form['elements']['MyFile']['fileinfo']['size'] . ' B';
   }
}
$Context-&gt;AddToDelegate('SetList', 'Process_MyExt', 'SetList_Process_MyExt');
</pre></blockquote>
<p>Try adding this to your extension's <code>default.php</code>, but replace
the text <code>MyExt</code> with your extension's key. The key is the name of
your extension with only alphanumeric and underscore characters. For
example, an extension named "Me and My_Monk3y" would have the key
<code>MeandMy_Monk3y</code>. Go ahead, try it. I'll wait.</p>
<p>Note that if your <code>Init_</code> or <code>Process_</code> function
adds a message to the Context's WarningCollector, the settings <em>will
not</em> be saved. This is a good easy way to invalidate the entire form. If
you want to send a non-error message, add to the global
<code>$NoticeCollector</code> object instead.</p>
<h3>Saving Settings</h3>
<p>Anything typed into a form will be saved by SetList. Type in stuff and
save it, then open Vanilla's <code>conf/settings.php</code> file. You should
see some lines at the end like this:</p>
<blockquote><pre>
$Configuration['MyExt.MyCheckbox'] = '1';
$Configuration['MyExt.MyText'] = 'I wrote some text.';
$Configuration['MyExt.MyTextarea'] = 'I wrote more text.';
$Configuration['MyExt.MySelect'] = '3';
$Configuration['MyExt.MyHidden'] = 'Something%2Bsneaky';
</pre></blockquote>
<p>The config key for each value is <code>YourExtensionKey.MySetting</code>
(or something similar). This can be changed by specifying a
<code>configkey</code> attribute for an element:</p>
<blockquote><pre>
; In an INI file:
elements.MyText.configkey = "MYEXTENSION_MYTEXT"

-OR-

// In code:
$form['elements']['MyText']['configkey'] = 'MYEXTENSION_MYTEXT';

-RESULTS IN-

$Configuration['MYEXTENSION_MYTEXT'] = 'I wrote some text';
</pre></blockquote>
<h3>Order of Operation</h3>
<p>The actual value of an element can come from one of several different steps.
A value specified in a later step will destroy any value from a previous
step:</p>
<dl>
<dt>1. Value may be specified in the INI:
<dd><code>elements.MyText.value = "some text"</code></dd>
<dt>2. Value is read from Vanilla's configuration (if the config key already
exists)</dt>
<dt>3. Value may be assigned in your extension's <code>Init_</code>
function:</dt>
<dd><code>$form['elements']['MyText']['value'] = 'other text';</code></dd>
<dt>4. <em>Settings form is displayed to the user</em></dt>
<dt>5. <em>Settings form is submitted by the user</em></dt>
<dt>6. Value is read from the submitted settings form</dt>
<dt>7. Value may be changed in your extension's <code>Process_</code>
function:</dt>
<dd><code>$form['elements']['MyText']['value'] = 'another text';</code></dd>
<dt>8. <em>Value is saved to Vanilla's settings file</em></dt>
</dl>
<p>Keep in mind that steps 6 through 8 don't happen until a user submits a
form. Until that happens, SetList will not put your settings in the global
settings file. If you want default values added when your extension first
installs, you will have to handle that yourself.</p>
<h3>A Word About File Uploads</h3>
<p>If you want to use the file upload element, you should definitely read the
PHP manual section about
<a href="http://www.php.net/manual/en/features.file-upload.post-method.php">POST
method uploads</a>. Don't come back here until you've read it. Seriously.</p>
<p>Now that you've read that important information, there are a few tasks
that SetList will take care of for you automatically:</p>
<ul>
<li>If you have a file upload element in your settings form, SetList will
automatically set the form encoding type to "multipart/form-data".</li>
<li>If you have a file upload element called "MyFile", SetList will shove the
contents of <code>$_FILES['MyFile']</code> into
<code>$form['elements']['MyFile']['fileinfo']</code> for your convenience.
You can access either one in your script.</li>
<li>File upload elements have no "value". That means you will never see an
entry in Vanilla's <code>conf/settings.php</code> file for the "MyFile"
setting.</li>
<li>SetList will automatically delete the file in
<code>$_FILES['MyFile']['tmp_name']</code> after your
<code>SetList_Process_MyExt()</code> function returns.</li>
</ul>
<p>SetList <b>will not</b> move the file, copy the file, open the file or do
anything to the file other than delete the temporary copy after your
processing function is finished. Doing anything useful with the uploaded file
is entirely up to your extension.</p>
<h3>Other Tricks</h3>
<p>To prevent an element from saving to config, use the <code>nosave</code>
attribute:</p>
<blockquote><pre>
; In an INI file:
elements.MyText.nosave = 1

-OR-

// In code:
$form['elements']['MyText']['nosave'] = 1;
</pre></blockquote>
<p>To prevent an element from rendering AND saving, use the
<code>norender</code> attribute:</p>
<blockquote><pre>
; In an INI file:
elements.MyText.norender = 1

-OR-

// In code:
$form['elements']['MyText']['norender'] = 1;
</pre></blockquote>


<hr />
<h2 id="translators">How Translators Use SetList</h2>
<p>Translating the <a href="#extensions">example form above</a> is as easy as
translating any other text in Vanilla. To translate the above form into
Spanish, just add the following lines to Vanilla's
<code>languages/Spanish/definitions.php</code> file:</p>
<blockquote><pre>
$Context-&gt;Dictionary['MyExt.MyHeader.label'] = 'Esta es una de cabecera';
$Context-&gt;Dictionary['MyExt.MyHeader.description'] = 'Este es el texto de la descripción de las cosas aquí.';
$Context-&gt;Dictionary['MyExt.MyCheckbox.label'] = 'Marque esta casilla';
$Context-&gt;Dictionary['MyExt.MyCheckbox.description'] = 'Si marca esta casilla, que serán revisados.';
$Context-&gt;Dictionary['MyExt.MyText.label'] = 'Algunos texto aquí';
$Context-&gt;Dictionary['MyExt.MyText.description'] = 'Se trata de una única línea de entrada de texto.';
$Context-&gt;Dictionary['MyExt.MyTextarea.label'] = 'Una gran cantidad de texto aquí';
$Context-&gt;Dictionary['MyExt.MyTextarea.description'] = 'Se trata de una línea de 15-cuadro de texto.';
$Context-&gt;Dictionary['MyExt.MySelect.label'] = 'Seleccione algo';
$Context-&gt;Dictionary['MyExt.MySelect.description'] = 'Se trata de una caja de selección.';
$Context-&gt;Dictionary['MyExt.MySelect.First.label'] = 'Primera';
$Context-&gt;Dictionary['MyExt.MySelect.Second.label'] = 'Segunda';
$Context-&gt;Dictionary['MyExt.MySelect.Third.label'] = 'Tercera';
$Context-&gt;Dictionary['MyExt.MyFile.label'] = 'Subir archivo';
$Context-&gt;Dictionary['MyExt.MyFile.description'] = 'Subir un archivo aquí.';
</pre></blockquote>
<p>Keep in mind that if the extension specifies error messages or other text
in the code, you should translate those too:</p>
<blockquote><pre>
$Context-&gt;Dictionary['MyExt.IP'] = 'Su dirección IP';
$Context-&gt;Dictionary['MyExt.ErrorThird'] = 'Usted debe elegir la tercera opción, porque lo digo.';
$Context-&gt;Dictionary['MyExt.FileSize'] = 'Tamaño del archivo:';
</pre></blockquote>

<hr />
<h2 id="themes">How Theme Writers Use SetList</h2>
<p>Theming the SetList form is just as easy as theming any other Vanilla
settings form. The template file is named
<code>settings_setlist_form.php</code> and should go in your theme's directory
with all of the other template files.</p>
<p>The easiest way to understand the template is to open it up and take a look
(it's not very complicated). The most important part is the loop that renders
the form elements:</p>
<blockquote><pre>
$elementKeys = array_keys($this-&gt;formData['elements']);
foreach ($elementKeys as $element) {
   if ( $this-&gt;shouldRender($element) ) {
      echo '
         &lt;li&gt;
            ' . $this-&gt;renderLabel($element) . '
            ' . $this-&gt;renderElementHtml($element) . '
            ' . $this-&gt;renderDescription($element) . '
         &lt;li&gt;
   }
}
</pre></blockquote>
<p>To render an HTML element, call <code>renderElementHtml()</code> and pass
in that element's key, likewise for the label and the description. The label
will come wrapped in <code>&lt;label&gt;&lt;/label&gt;</code> tags. The
description will come wrapped in <code>&lt;p&gt;&lt;/p&gt;</code> tags with the
attribute <code>class="Description"</code>.</p>
<p>The element, the label, and the description are all rendered seperately
and you can render them in any order you want. <strong>There are two
exceptions:</strong> First, for any checkbox element the label will be
rendered by <code>renderElementHtml()</code> along with the checkbox element
itself. This is to keep consistency with the look of Vanilla's own settings
forms. Calling <code>renderLabel()</code> for a checkbox element will return
an empty string. Second, a "header" is not strictly an HTML element, so it has
no value. The text of a header's "label" is used as the header text. Calling
<code>renderLabel()</code> for a header element will return an empty
string.</p>

<hr />
<h2 id="uninstall">Uninstalling SetList</h2>
<p>Had enough? Just disable the SetList extension in Vanilla and delete the
SetList directory from the extensions directory. That's all.</p>

<hr />
<h2 id="develop">Development</h2>
<p>SetList was written by
<a href="mailto:squirrel@digitalsquirrel.com">squirrel</a>. Updates are
available at the <a href="http://lussumo.com/addons/index.php?PostBackAction=AddOn&amp;AddOnID=308">Vanilla add-ons
site</a>. Part of the
<a href="http://code.google.com/p/vanilla-friends/">Vanilla Friends</a>
project.</p>

</body>
</html>

